package aceim.api.utils; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Random; import aceim.api.IProtocol; import aceim.api.dataentity.Buddy; import aceim.api.dataentity.Entity; import aceim.api.dataentity.ListFeature; import aceim.api.service.ApiConstants; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Bundle; import android.os.Environment; import android.os.Parcel; import android.os.Parcelable; /** * A utility class with useful methods. */ public final class Utils { private Utils(){} /** * Get IP address I'm currently online with. * * @return IP address in dot-separated form * @throws SocketException if something bad happened during IP address recovering */ public static String getMyIp() throws SocketException { List<String> ips = new LinkedList<String>(); for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) { NetworkInterface intf = en.nextElement(); for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) { InetAddress inetAddress = enumIpAddr.nextElement(); if (!inetAddress.isLoopbackAddress()) { ips.add(inetAddress.getHostAddress()); } } } return ips.get(0); } /** * Create SD card file instance for incoming file transfer. * * @param filename file name, without path * @param buddy file sender * @param modTime modification time (optional, set if greater than zero) * @return */ public static File createLocalFileForReceiving(String filename, Buddy buddy, long modTime) { File root = Environment.getExternalStorageDirectory(); File downloads; if (buddy != null) { downloads = new File(root, "AceIM" + File.separator + buddy.getOwnerUid() + File.separator + buddy.getProtocolUid() + "_" + buddy.getName()); } else { downloads = new File(root, "AceIM"); } downloads.mkdirs(); File file = new File(downloads, filename); if (file.exists()) { boolean deleted = file.delete(); if (!deleted) { file = new File(downloads, new Random().nextInt() + "_" + filename); } } if (modTime > 0) { file.setLastModified(modTime); } return file; } /** * Escape {@link ApiConstants#GENERAL_DIVIDER} instances with underline signs. * @param input input string * @return result */ public static String escapeGeneralDividers(String input) { return input.replace(ApiConstants.GENERAL_DIVIDER, '_'); } /** * Get typed array from resources and return it as integer aray. Used, for example, in {@link ListFeature} entities. * @param r resources * @param resourceId typed array's resource ID * @return integer array of resource IDs, or null if input resourceId is not valid */ public static int[] fillResources(Resources r, int resourceId) { if (resourceId < 0) { return null; } TypedArray ta = r.obtainTypedArray(resourceId); int[] targetArray = new int[ta.length()]; for (int i = 0; i < targetArray.length; i++) { targetArray[i] = ta.getResourceId(i, 0); } ta.recycle(); return targetArray; } /** * Convert map of parcelables to bundle. * @param map * @return */ public static final Bundle map2Bundle(Map<String, Parcelable> map) { if (map == null) { return null; } Bundle b = new Bundle(); for (String k : map.keySet()) { b.putParcelable(k, map.get(k)); } return b; } /** * Convers bundle with parcelables to map. * @param bundle * @return */ public static final Map<String, Parcelable> bundle2Map(Bundle bundle){ if (bundle == null) { return null; } Map<String, Parcelable> map = new HashMap<String, Parcelable>(); for (String k : bundle.keySet()){ map.put(k, bundle.getParcelable(k)); } return map; } /** * Scale and crop to square image, for usage with {@link IProtocol#uploadAccountPhoto(String)}. If input file is already less than required size, no additional processing is applied. * @param filePath filesystem path to image file, should be accessible * @param targetSize target side length in pixels * @return resized and cropped image, in a form of byte array */ public static byte[] scaleAccountIcon(String filePath, int targetSize) { BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeFile(filePath, o); if (o.outWidth > o.outHeight) { o.inSampleSize = o.outWidth / targetSize; } else { o.inSampleSize = o.outHeight / targetSize; } o.inJustDecodeBounds = false; ByteArrayOutputStream bo = new ByteArrayOutputStream(); try { Bitmap b = BitmapFactory.decodeFile(filePath, o); if (o.outHeight > targetSize || o.outWidth > targetSize) { Bitmap newb; if (o.outHeight < o.outWidth) { newb = Bitmap.createScaledBitmap(b, (int) (((targetSize + 0f)*o.outWidth)/o.outHeight), targetSize, false); } else { newb = Bitmap.createScaledBitmap(b, targetSize, (int) (((targetSize + 0f)*o.outHeight)/o.outWidth), false); } if (b != newb) { b.recycle(); b = newb; } if (b.getWidth() >= b.getHeight()){ newb = Bitmap.createBitmap(b, (int) ((b.getWidth() - b.getHeight()) / 2f), 0, b.getHeight(), b.getHeight()); } else { newb = Bitmap.createBitmap(b, 0, (int) ((b.getHeight() - b.getWidth()) / 2f), b.getWidth(), b.getWidth()); } b.recycle(); b = newb; } b.compress(Bitmap.CompressFormat.JPEG, 100, bo); b.recycle(); return bo.toByteArray(); } finally { try { bo.close(); } catch (IOException e) { Logger.log(e); } } } /** * Read {@link Entity} from a parcel, if entity class is known. * @param in parcel container * @param cls entity class * @return unparcelled entity */ @SuppressWarnings("unchecked") public static final <T extends Parcelable> T unparcelEntity(Parcel in, Class<T> cls) { String className = in.readString(); try { if (className.equals(cls.getName())) { return cls.getConstructor(Parcel.class).newInstance(in); } } catch (Exception e) { Logger.log(e); } return (T) unparcelUnknownEntity(in, className); } /** * Read {@link Entity} from a parcel by class name * @param in parcel container * @param className entity class name * @return recovered entity, or null if class name is invalid, or the class entity cannot be created */ @SuppressWarnings("unchecked") public static Entity unparcelUnknownEntity(Parcel in, String className) { try { Class<? extends Entity> cls = (Class<? extends Entity>) Class.forName(className); Class<?>[] paramTypes = { Parcel.class }; Constructor<? extends Entity> constructor = cls.getConstructor(paramTypes); return constructor.newInstance(in); } catch (Exception e) { Logger.log(e); } return null; } /** * Escape XML characters in a string. * @param input * @return */ public static String escapeXMLString(String input) { if (input == null) return null; return input.replaceAll("&", "&").replaceAll("'", "'").replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">"); } /** * Unescape XML characters in a string. * @param input * @return */ public static String unescapeXMLString(String input) { if (input == null) return null; return input.replaceAll("&", "&").replaceAll("'", "'").replaceAll(""", "\"").replaceAll("<", "<").replaceAll(">", ">"); } }